package net.paoding.rose.jade.statement.jexl;


import net.paoding.rose.jade.excetion.JadeException;
import net.paoding.rose.jade.statement.jexl.analysis.Analysis;
import net.paoding.rose.jade.statement.jexl.analysis.impl.ForAnalysis;
import net.paoding.rose.jade.statement.jexl.analysis.impl.IfElseAnalysis;
import net.paoding.rose.jade.statement.jexl.analysis.impl.JoinAnalysis;
import net.paoding.rose.jade.statement.jexl.analysis.impl.NameSpaceAnalysis;
import net.paoding.rose.jade.statement.jexl.analysis.impl.TxtAnalysis;
import net.paoding.rose.jade.statement.jexl.analysis.impl.VariableAnalysis;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;

/**
 * jexl3 表达式解析
 * @author fusheng.zhang
 * @date 2022-03-03 14:48:35
 */
public class Jexl3Analysis {

    /**
     * 带解析语句 源语
     */
    private final String sourceLanguage;
    /**
     * 编译位置
     */
    private int position;
    /**
     * 组装位置
     */
    private int fromIndex;

    private final Matcher sourceLanguageMatcher;
    /**
     * #else Matcher
     */
    private Matcher elseMatcher;

    /**
     * #_ namespace matcher
     */
    private Matcher namespaceMatcher;

    private final List<Analysis> result = new ArrayList<>();

    public Jexl3Analysis(String sourceLanguage) {
        this.sourceLanguage = sourceLanguage;
        this.sourceLanguageMatcher = Jexl3Constant.PATTERN_KEYWORD.matcher(this.sourceLanguage);
    }


    public List<Analysis> compile() {
        try {
            while (sourceLanguageMatcher.find(position)) {
                position = sourceLanguageMatcher.end();
                String expr = sourceLanguageMatcher.group(1),
                        group = sourceLanguageMatcher.group(2),
                        keyword = sourceLanguageMatcher.group(3);
                boolean isIn = false;
                if (Objects.nonNull(expr)) {
                    if (sourceLanguageMatcher.start() > fromIndex) {
                        TxtAnalysis txtAnalysis = new TxtAnalysis(sourceLanguage.substring(fromIndex, sourceLanguageMatcher.start()));
                        isIn = Jexl3Constant.PATTERN_IN.matcher(txtAnalysis.getTxt()).find();
                        this.result.add(txtAnalysis);
                    }
                    // 创建  :expr | $expr 形式的子句
                    if (expr.charAt(0) == Jexl3Constant.KEYWORD_$_) {
                        this.result.add(new JoinAnalysis().setJoinExp(expr));
                    } else {
                        this.result.add(new VariableAnalysis(expr, isIn));
                    }
                } else if (Objects.nonNull(group)) {
                    this.result.addAll(new Jexl3Analysis(group).compile());
                } else if (Objects.isNull(keyword)) {
                    expr = parentheses();
                    if (Objects.nonNull(expr)) {
                        if (sourceLanguageMatcher.start() > fromIndex) {
                            TxtAnalysis txtAnalysis = new TxtAnalysis(sourceLanguage.substring(fromIndex, sourceLanguageMatcher.start()));
                            isIn = Jexl3Constant.PATTERN_IN.matcher(txtAnalysis.getTxt()).find();
                            this.result.add(txtAnalysis);
                        }
                        this.result.add(new VariableAnalysis(expr, isIn));
                    }
                } else {
                    switch (keyword) {
                        case Jexl3Constant.KEYWORD_IF: ifElse();
                            break;
                        case Jexl3Constant.KEYWORD_FOR: forLoop();
                            break;
                        case Jexl3Constant.KEYWORD_SHARP: case Jexl3Constant.KEYWORD_JOIN: join();
                            break;
                        case Jexl3Constant.KEYWORD_NAME_SPACE: namespace();
                            break;
                    }
                }
                fromIndex = position;
            }
            // 写入 PREFIX 字符后的内容。
            if (fromIndex < sourceLanguage.length()) {
                this.result.add(new TxtAnalysis(sourceLanguage.substring(fromIndex)));
            }
        } catch (Exception e) {
            throw new JadeException(e.getMessage(), e);
        }
        return this.result;
    }

    private void namespace() {
        if (Objects.isNull(this.namespaceMatcher)) {
            this.namespaceMatcher = Jexl3Constant.PATTERN_NAME_SPACE.matcher(this.sourceLanguage);
        }
        if (!this.namespaceMatcher.find(this.position)) return;
        int start = sourceLanguageMatcher.start();
        if (start > fromIndex) {
            this.result.add(new TxtAnalysis(sourceLanguage.substring(fromIndex, start)));
        }
        String namespace = this.namespaceMatcher.group(1);
        String method = this.namespaceMatcher.group(2);
        this.position = this.namespaceMatcher.end();
        String methodParam = parentheses();
        this.result.add(new NameSpaceAnalysis(namespace, method, methodParam));
    }

    private void join() {
        String joinStr = parentheses();
        if (Objects.isNull(joinStr)) return;
        int start = sourceLanguageMatcher.start();
        if (start > fromIndex) {
            this.result.add(new TxtAnalysis(sourceLanguage.substring(fromIndex, start)));
        }
        this.result.add(new JoinAnalysis().setJoinExp(joinStr));
    }

    private void forLoop() {
        // 获取括号内容
        String forCondition = parentheses();
        if (Objects.isNull(forCondition)) return;
        int start = sourceLanguageMatcher.start();
        if (start > fromIndex) {
            this.result.add(new TxtAnalysis(sourceLanguage.substring(fromIndex, start)));
        }
        ForAnalysis forAnalysis = new ForAnalysis();

        // 解析  variant in :expr 表达式
        Matcher matcherIn = Jexl3Constant.PATTERN_IN.matcher(forCondition);
        if (matcherIn.matches()) {
            forAnalysis.setVariable(matcherIn.group(1));
            forCondition = matcherIn.group(2);
        }
        forAnalysis.setForCondition(forCondition);
        // for 执行语句{}
        String forExecute = braces();
        if (Objects.nonNull(forExecute)) {
            List<Analysis> analysisResult = new Jexl3Analysis(forExecute).compile();
            if (analysisResult.isEmpty()) analysisResult.add(new TxtAnalysis(forExecute));
            forAnalysis.setForExecutes(analysisResult);
        }
        this.result.add(forAnalysis);
    }

    private void ifElse() {
        // 获取if条件语句 ()
        String ifCondition = parentheses();
        if (Objects.isNull(ifCondition)) return;
        int start = sourceLanguageMatcher.start();
        if (start > fromIndex) {
            this.result.add(new TxtAnalysis(sourceLanguage.substring(fromIndex, start)));
        }
        // 构建 if 表达式
        IfElseAnalysis ifElseAnalysis = new IfElseAnalysis();
        List<Analysis> ifConditions = new Jexl3Analysis(ifCondition).compile();
        if (ifConditions.isEmpty()) ifConditions.add(new TxtAnalysis(ifCondition));
        ifElseAnalysis.setIfConditions(ifConditions);
        // 获取if执行语句 {}
        String ifExecute = braces();
        if (Objects.nonNull(ifExecute)) {
            List<Analysis> analysisResult = new Jexl3Analysis(ifExecute).compile();
            if (analysisResult.isEmpty()) analysisResult.add(new TxtAnalysis(ifCondition));
            ifElseAnalysis.setIfExecutes(analysisResult);
        }
        // 判断是否存在 else,如果是则步进5
        if (this.startElse()) {
            this.position += 5;
            // 获取 else执行语句{}
            String elseExecute = braces();
            if (Objects.nonNull(elseExecute)) {
                List<Analysis> analysesResult = new Jexl3Analysis(elseExecute).compile();
                if (analysesResult.isEmpty()) analysesResult.add(new TxtAnalysis(ifCondition));
                ifElseAnalysis.setElseExecutes(analysesResult);
            } else {
                this.position -= 5;
            }
        }
        this.result.add(ifElseAnalysis);
    }

    public boolean startElse() {
        if (Objects.isNull(this.elseMatcher)) this.elseMatcher = Jexl3Constant.PATTERN_ELSE.matcher(sourceLanguage);
        return elseMatcher.find(this.position) && elseMatcher.group().equals(Jexl3Constant.KEYWORD_ELSE);
    }

    /**
     * 匹配大括号
     * @return
     */
    private String braces() {
        return findBrace('{', '}');
    }

    /**
     * 匹配小括号
     * @return
     */
    private String parentheses() {
        return findBrace('(', ')');
    }


    /**
     * 查找匹配的左括号, 忽略之前的空白字符。
     * <p>
     * 如果未找到匹配的左括号，函数返回 -1.
     * @param chLeft    - 匹配的左括号
     * @param fromIndex - 查找的起始位置
     * @return 左括号的位置, 如果未找到匹配的左括号，函数返回 -1.
     */
    private int findLeftBrace(char chLeft, int fromIndex) {
        // 查找出现的左括号。
        for (int index = fromIndex; index < sourceLanguage.length(); index++) {
            char ch = sourceLanguage.charAt(index);
            // 如果出现左括号，返回。 如果出现非空白字符，返回 - 1.
            if (ch == chLeft) {
                return index;
            } else if (!Character.isWhitespace(ch)) {
                return -1;
            }
        }
        // 没有找到匹配的括号。
        return -1;
    }

    /**
     * 查找匹配的右括号, 可以用于匹配 '{}', '[]', '()' 括号对。
     * <p>
     * 如果未找到匹配的右括号，函数返回 -1.
     * @param chLeft    - 匹配的左括号
     * @param chRight   - 匹配的右括号
     * @param fromIndex - 查找的起始位置
     * @return 右括号的位置, 如果未找到匹配的右括号，函数返回 -1.
     */
    private int findRightBrace(char chLeft, char chRight, int fromIndex) {
        // 记录括号层级。
        int level = 0;
        // 查找匹配的右括号。
        for (int index = fromIndex; index < sourceLanguage.length(); index++) {
            char ch = sourceLanguage.charAt(index);
            // 如果出现左括号，重叠级别增加。如果出现右括号，重叠级别降低。
            if (ch == chLeft) {
                level++;
            } else if (ch == chRight) {
                // 找到右括号。
                if (level == 0) return index;
                level--;
            }
        }
        // 没有找到匹配的括号。
        return -1;
    }

    /**
     * 从当前位置查找匹配的一对括号, 并返回内容。
     * <p>
     * 如果有匹配的括号, 返回后的当前位置指向匹配的右括号后一个字符。
     * @param chLeft  - 匹配的左括号
     * @param chRight - 匹配的右括号
     * @return 返回括号内容, 如果没有括号匹配, 返回 <code>null</code>.
     */
    private String findBrace(char chLeft, char chRight) {
        // 从当前位置查找查找匹配的  (...)
        int left = findLeftBrace(chLeft, position);
        if (left >= position) {
            int start = left + 1;
            int end = findRightBrace(chLeft, chRight, start);
            if (end >= start) {
                // 当前位置指向匹配的右括号后一个字符
                position = end + 1;
                // 返回匹配的括号内容
                return sourceLanguage.substring(start, end);
            }
        }
        return null; // 没有  (...) 匹配
    }

}
